package com.googlecode.restitory.gae.http;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;

import com.googlecode.mycontainer.commons.file.PathUtil;

import flexjson.JSON;

public class Request {

	private Type type;

	private String path;

	private final NamePairs params = new NamePairs();

	private final NamePairs headers = new NamePairs();

	private Content content;

	public Request() {

	}

	public Request(Type type, String path) {
		this.type = type;
		this.path = path;
	}

	public Type getType() {
		return type;
	}

	public Request setType(Type type) {
		this.type = type;
		return this;
	}

	public String getPath() {
		return path;
	}

	public Request setPath(String path) {
		this.path = path;
		return this;
	}

	public Content getContent() {
		return content;
	}

	public String getContenType() {
		return headers.getContentType();
	}

	public String getMediaType() {
		return headers.getMediaType();
	}

	public String getCharset() {
		return headers.getCharset();
	}

	public Request setContent(String text) {
		String mediaType = getMediaType();
		String charset = getCharset();
		return setContent(Content.createFromString(mediaType, charset, text));
	}

	public Request setContent(char... charArray) {
		String mediaType = getMediaType();
		String charset = getCharset();
		return setContent(Content.createFromString(mediaType, charset, charArray));
	}

	public Request setContent(Content content) {
		if (content == null) {
			this.content = null;
			return this;
		}
		String mediaType = getMediaType();
		String charset = getCharset();

		if (mediaType == null) {
			throw new RuntimeException("mediaType is required");
		}
		if (!mediaType.equals(content.getMediaType())) {
			throw new RuntimeException("mediaType wrong: " + mediaType + ", " + content.getMediaType());
		}
		if (charset == null && content.getCharset() != null) {
			throw new RuntimeException("charset wrong: " + charset + ", " + content.getCharset());
		}
		if (charset != null && !charset.equals(content.getCharset())) {
			throw new RuntimeException("charset wrong: " + charset + ", " + content.getCharset());
		}

		this.content = content;
		return this;
	}

	public NamePairs getParams() {
		return params;
	}

	public NamePairs getHeaders() {
		return headers;
	}

	@Override
	public String toString() {
		String ret = "" + type + " " + path;
		String p = params.toString();
		if (p.length() > 0) {
			ret += "?" + params;
		}
		if (content != null) {
			ret += " " + content;
		}
		return ret;
	}

	@JSON(include = false)
	public String getContentType() {
		return headers.getContentType();
	}

	public Request copy() {
		Request ret = new Request().setPath(path).setType(type);
		ret.getHeaders().setPairs(getHeaders().getPairs());
		ret.getParams().setPairs(getParams().getPairs());
		ret.setContent(content);
		return ret;
	}

	public Request setHeader(String key, String value) {
		headers.set(key, value);
		return this;
	}

	public Request setAccept(String value) {
		headers.set("Accept", value);
		return this;
	}

	public Request setContentType(String value) {
		headers.set("Content-Type", value);
		return this;
	}

	public Request setContent(byte... bytes) {
		return setContent(Content.create(getMediaType(), getCharset(), bytes));
	}

	public URI toURI(String repository) {
		try {
			return new URI(repository + encodeRequestLine());
		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}
	}

	private String encodeRequestLine() {
		String p = params.encodeParams();
		if (p.length() > 0) {
			return "" + path + "?" + p;
		} else {
			return "" + path;
		}
	}

	public Request addParam(String key, String value) {
		params.add(key, value);
		return this;
	}

	public Request addHeader(String key, String value) {
		headers.add(key, value);
		return this;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((content == null) ? 0 : content.hashCode());
		result = prime * result + ((headers == null) ? 0 : headers.hashCode());
		result = prime * result + ((params == null) ? 0 : params.hashCode());
		result = prime * result + ((path == null) ? 0 : path.hashCode());
		result = prime * result + ((type == null) ? 0 : type.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Request other = (Request) obj;
		if (content == null) {
			if (other.content != null)
				return false;
		} else if (!content.equals(other.content))
			return false;
		if (headers == null) {
			if (other.headers != null)
				return false;
		} else if (!headers.equals(other.headers))
			return false;
		if (params == null) {
			if (other.params != null)
				return false;
		} else if (!params.equals(other.params))
			return false;
		if (path == null) {
			if (other.path != null)
				return false;
		} else if (!path.equals(other.path))
			return false;
		if (type != other.type)
			return false;
		return true;
	}

	public URL toURL() {
		try {
			return new URL(encodeRequestLine());
		} catch (MalformedURLException e) {
			throw new RuntimeException(e);
		}
	}

	public Request appendPath(String path) {
		return setPath(PathUtil.concatPath(this.path, path));
	}

	public boolean isBinary() {
		return headers.isBinary();
	}

}
